package com.lengxf.pay.wx;

import cn.hutool.core.util.StrUtil;
import com.lengxf.pay.common.PayException;
import com.lengxf.pay.common.bean.*;
import com.lengxf.pay.common.config.GlobalObjectMapper;
import com.lengxf.pay.common.service.AbstractBasePayService;
import com.lengxf.pay.common.service.AppPayService;
import com.lengxf.pay.common.service.BarcodePayService;
import com.lengxf.pay.wx.bean.AppPayModel;
import com.lengxf.pay.wx.bean.BarcodeResult;
import com.lengxf.pay.wx.config.WX_PayConfig;
import com.lengxf.pay.wx.config.WX_TransactionType;
import com.lengxf.pay.wx.service.*;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;

import java.util.Map;

/**
 * 微信
 * By Lengxf 2023-10-26 11:23:21
 **/
public class WX_PayService extends AbstractBasePayService<WX_PayConfig> implements AppPayService, BarcodePayService {

    private final Config config;

    public WX_PayService(WX_PayConfig wxPayConfig) {
        super(wxPayConfig);
        config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayConfig.getMerchantId())
                .privateKey(wxPayConfig.keyPrivate())
                .merchantSerialNumber(wxPayConfig.getMchSerialNo())
                .apiV3Key(wxPayConfig.getApiV3Key())
                .build();
    }

    @Override
    public String toPay(PayOrder payOrder) {
        String payWay = StrUtil.isBlank(payOrder.getPayWay()) ? WX_TransactionType.H5.toString() : payOrder.getPayWay();
        WX_TransactionType wxTransactionType = WX_TransactionType.toEnum(payWay);
        return switch (wxTransactionType) {
            case H5 -> new WxH5PayService(config, getPayConfig()).qrPay(payOrder);
            case NATIVE -> new WxNativePayService(config, getPayConfig()).qrPay(payOrder);
            default -> throw new PayException("支付方式调用错误");
        };
    }

    @Override
    public Map<String, Object> appPay(PayOrder payOrder) {
        WX_TransactionType wxTransactionType = WX_TransactionType.toEnum(payOrder.getPayWay());
        String prepayId = switch (wxTransactionType) {
            case JSAPI -> new WxJsapiPayService(config, getPayConfig()).prepayId(payOrder);
            case APP -> new WxAppPayService(config, getPayConfig()).prepayId(payOrder);
            default -> throw new PayException("支付方式调用错误");
        };
        AppPayModel appPayModel = new AppPayModel(getPayConfig().getAppid(), prepayId);
        appPayModel.setPaySign(createSign(appPayModel.buildSignStr()));
        return appPayModel.toMap();
    }

    @Override
    public Map<String, Object> query(QueryOrder queryOrder) {
        NativePayService payService = new NativePayService.Builder().config(config).build();
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(getPayConfig().getMerchantId());
        request.setOutTradeNo(queryOrder.getOrderNo());
        Transaction transaction = payService.queryOrderByOutTradeNo(request);
        return GlobalObjectMapper.beanToMap(transaction);
    }

    @Override
    public Map<String, Object> refund(RefundOrder refundOrder) {
        RefundService refundService = new RefundService.Builder().config(config).build();
        CreateRequest createRequest = new CreateRequest();
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(refundOrder.getAmount().longValue());
        createRequest.setAmount(amountReq);
        createRequest.setOutRefundNo(refundOrder.getRefundOrderNo());
        createRequest.setOutTradeNo(refundOrder.getOrderNo());
//        createRequest.setNotifyUrl("");
        Refund refund = refundService.create(createRequest);
        return GlobalObjectMapper.beanToMap(refund);
    }

    @Override
    public Map<String, Object> refundQuery(RefundOrderQuery refundOrderQuery) {
        RefundService refundService = new RefundService.Builder().config(config).build();
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(refundOrderQuery.getOrderNo());
        Refund refund = refundService.queryByOutRefundNo(request);
        return GlobalObjectMapper.beanToMap(refund);
    }

    @Override
    public Map<String, Object> payBack(CallBackParam callBackParam) {
        Transaction transaction = validationCallBack(callBackParam, Transaction.class);
        return GlobalObjectMapper.beanToMap(transaction);
    }

    @Override
    public Map<String, Object> refundBack(CallBackParam callBackParam) {
        Refund refund = validationCallBack(callBackParam, Refund.class);
        return GlobalObjectMapper.beanToMap(refund);
    }

    private <R> R validationCallBack(CallBackParam callBackParam, Class<R> clazz) {
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(callBackParam.getHeaderValue("Wechatpay-Serial"))
                .nonce(callBackParam.getHeaderValue("Wechatpay-Nonce"))
                .signature(callBackParam.getHeaderValue("Wechatpay-Signature"))
                .timestamp(callBackParam.getHeaderValue("Wechatpay-Timestamp"))
                .body(callBackParam.getBody())
                .build();
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(getPayConfig().getMerchantId())
                .privateKey(getPayConfig().getKeyPrivate())
                .merchantSerialNumber(getPayConfig().getMchSerialNo())
                .apiV3Key(getPayConfig().getApiV3Key())
                .build();
        NotificationParser parser = new NotificationParser(config);
        try {
            return parser.parse(requestParam, clazz);
        } catch (ValidationException e) {
            log.error("微信验签失败 : ", e);
            throw new PayException(e.getMessage());
        }
    }

    @Override
    public Map<String, Object> barcodeCay(PayOrder payOrder) {
        WxBarcodePayService wxBarcodePayService = new WxBarcodePayService(getPayConfig());
        BarcodeResult barcodeResult = wxBarcodePayService.barcodePay(payOrder);
        return GlobalObjectMapper.beanToMap(barcodeResult);
    }

}
